package com.guhecloud.step.definition.runtime;

import com.guhecloud.step.definition.runtime.def.ExtensionDef;
import com.sun.istack.internal.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

@Slf4j
public class ExtensionInvocationHandler <Ext extends IDomainExtension, R> implements InvocationHandler {
    private final Class<Ext> extInterface;
    private final IDomainModel model;
    private final IReducer<R> reducer;
    private final Ext defaultExt;
    private int timeoutInMs;
    ExtensionInvocationHandler(@NotNull Class<Ext> extInterface, @NotNull IDomainModel model, IReducer<R> reducer, Ext defaultExt, int timeoutInMs) {
        this.extInterface = extInterface;
        this.model = model;
        this.reducer = reducer;
        this.defaultExt = defaultExt;
        this.timeoutInMs = timeoutInMs;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 获取某一扩展点的所有实例
        List<ExtensionDef> effectiveExts = InternalIndexer.findEffectiveExtensions(extInterface, model, reducer == null);
        if (effectiveExts.isEmpty()) {
            if (defaultExt == null) {
                // 扩展点方法的返回值不能是int/boolean等，否则会抛出NPE!
                return null;
            }

            effectiveExts.add(new ExtensionDef(defaultExt));
        }
        List<R> accumulatedResults = new ArrayList<>(effectiveExts.size());
        R result = null;
        for (ExtensionDef extensionDef : effectiveExts) {
            result = invokeExtension(extensionDef, method, args);
            accumulatedResults.add(result);

            if (reducer == null || reducer.shouldStop(accumulatedResults)) {
                break;
            }
        }

        if (reducer != null) {
            // reducer决定最终的返回值
            return this.reducer.reduce(accumulatedResults);
        }

        // 没有reducer，那么返回最后一个扩展点的执行结果
        return result;
    }


    private R invokeExtension(ExtensionDef extensionDef, final Method method, Object[] args) throws Throwable {
        try {
            return invokeExtensionMethod(extensionDef, method, args);
        } catch (InvocationTargetException e) {
            // 此处接收被调用方法内部未被捕获的异常：扩展点里抛出异常
            throw e.getTargetException();
        } catch (TimeoutException e) {
            // java里的TimeoutException继承Exception，需要转为ExtTimeoutException，否则上层看到的异常是 UndeclaredThrowableException
            throw e;
        } catch (RejectedExecutionException e) {
            throw e;
        } catch (Throwable e) {
            throw e;
        }
    }
    private R invokeExtensionMethod(ExtensionDef extensionDef, Method method, Object[] args) throws Throwable {
        IDomainExtension extInstance = extensionDef.getExtensionBean();
        if (timeoutInMs > 0) {
            return invokeExtensionMethodWithTimeout(extInstance, method, args, timeoutInMs);
        }
        R result = (R) method.invoke(extInstance, args);
        return result;
    }

    private static ExecutorService extInvokeTimerExecutor = new ThreadPoolExecutor(
            10,
            System.getProperty("invokeExtMaxPoolSize") != null ? Integer.valueOf(System.getProperty("invokeExtMaxPoolSize")) : 50,
            5L, TimeUnit.MINUTES, // 线程5m内idle，则被回收
            new SynchronousQueue<>(), // 无队列，线程池满后新请求进来则 RejectedExecutionException
            new NamedThreadFactory("ExtInvokeTimer", false)); // daemon=false, shutdown时等待扩展点执行完毕


    private R invokeExtensionMethodWithTimeout(IDomainExtension extInstance, Method method, Object[] args, final int timeoutInMs) throws Throwable {
        // 切换到线程池ThreadLocal会失效，目前ThreadLocal只有MDC
        Map<String, String> mdcContext = MDC.getCopyOfContextMap();
        Future<R> future = extInvokeTimerExecutor.submit(() -> {
            MDC.setContextMap(mdcContext); // 手动继承前面线程的MDC
            try {
                return (R) method.invoke(extInstance, args);
            } finally {
                MDC.clear();
            }
        });

        try {
            R result = future.get(timeoutInMs, TimeUnit.MILLISECONDS);
            return result;
        } catch (TimeoutException e) {
            if (!future.isCancelled()) {
                future.cancel(true);
            }

            throw e;
        } catch (ExecutionException e) {
            // future的异常机制，这里尽可能把真实的异常抛出去
            throw e.getCause() != null ? e.getCause() : e;
        }
    }


    Ext createProxy() {
        return (Ext) Proxy.newProxyInstance(extInterface.getClassLoader(), new Class[]{this.extInterface}, this);
    }



}
